{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 手写识别图像识别前馈网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本实验主要内容是进行手写字体图像的识别，用到的框架主要包括：MindSpore0.5.0，主要用于深度学习算法的构建，本实验以开源的手写字体数据集为基础，基于MindSpore深度学习库对手写字体进行识别。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入实验环境"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#导入相关依赖库\n",
    "import  os\n",
    "# os.environ['DEVICE_ID'] = '7'\n",
    "\n",
    "from matplotlib import pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import mindspore as ms\n",
    "import mindspore.context as context\n",
    "import mindspore.dataset as ds\n",
    "import mindspore.dataset.transforms.c_transforms as C\n",
    "import mindspore.dataset.vision.c_transforms as CV\n",
    "from mindspore.nn.metrics import Accuracy\n",
    "\n",
    "from mindspore import nn\n",
    "from mindspore.train import Model\n",
    "from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitor, TimeMonitor\n",
    "\n",
    "context.set_context(mode=context.GRAPH_MODE, device_target='Ascend')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入实验数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练数据集数量： 60000\n",
      "测试数据集数量： 10000\n",
      "图像长/宽/通道数： (28, 28, 1)\n",
      "一张图像的标签样式： 7\n"
     ]
    }
   ],
   "source": [
    "ds_train = ds.MnistDataset(os.path.join(r'./MNIST', \"train\"))\n",
    "ds_test = ds.MnistDataset(os.path.join(r'./MNIST', \"test\")) \n",
    "\n",
    "print('训练数据集数量：',ds_train.get_dataset_size())\n",
    "print('测试数据集数量：',ds_test.get_dataset_size())\n",
    "image=ds_train.create_dict_iterator().__next__()\n",
    "print('图像长/宽/通道数：',image['image'].shape)\n",
    "print('一张图像的标签样式：',image['label'])    #一共10类，用0-9的数字表达类别。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_DIR_TRAIN = \"MNIST/train\" # 训练集信息\n",
    "DATA_DIR_TEST = \"MNIST/test\" # 测试集信息\n",
    "\n",
    "def create_dataset(training=True, batch_size=128, resize=(28, 28),\n",
    "                   rescale=1/(255*0.3081), shift=-0.1307/0.3081, buffer_size=64):\n",
    "    ds = ms.dataset.MnistDataset(DATA_DIR_TRAIN if training else DATA_DIR_TEST)\n",
    "    \n",
    "    # define map operations\n",
    "    resize_op = CV.Resize(resize)\n",
    "    rescale_op = CV.Rescale(rescale, shift)\n",
    "    hwc2chw_op = CV.HWC2CHW()\n",
    "    \n",
    "    # apply map operations on images\n",
    "    ds = ds.map(input_columns=\"image\", operations=[resize_op, rescale_op, hwc2chw_op])\n",
    "    ds = ds.map(input_columns=\"label\", operations=C.TypeCast(ms.int32))\n",
    "    \n",
    "    ds = ds.shuffle(buffer_size=buffer_size)\n",
    "    ds = ds.batch(batch_size, drop_remainder=True)\n",
    "#     ds = ds.repeat(num_epoch)\n",
    "    \n",
    "    return ds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#显示前10张图片以及对应标签,检查图片是否是正确的数据集\n",
    "ds = create_dataset(training=False)\n",
    "data = ds.create_dict_iterator().__next__()\n",
    "images = data['image'].asnumpy()\n",
    "labels = data['label'].asnumpy()\n",
    "\n",
    "for i in range(1,11):\n",
    "    plt.subplot(2, 5, i)\n",
    "    plt.imshow(np.squeeze(images[i]))\n",
    "    plt.title('Number: %s' % labels[i])\n",
    "    plt.xticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 手写字体图像识别建模"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据集准备完成，接下来我们就需要构建训练模型，我们首先需要建立初始化的神经网络。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#创建模型。模型包括5个卷积层和RELU激活函数，一个全连接输出层并使用softmax进行多分类，共分成（0-9）10类\n",
    "class ForwardNN(nn.Cell):      \n",
    "    def __init__(self):\n",
    "        super(ForwardNN, self).__init__()\n",
    "        self.flatten = nn.Flatten()\n",
    "        self.relu = nn.ReLU()\n",
    "        self.fc1 = nn.Dense(784, 512, activation='relu')\n",
    "        self.fc2 = nn.Dense(512, 256, activation='relu')\n",
    "        self.fc3 = nn.Dense(256, 128, activation='relu')\n",
    "        self.fc4 = nn.Dense(128, 64, activation='relu')\n",
    "        self.fc5 = nn.Dense(64, 32, activation='relu')\n",
    "        self.fc6 = nn.Dense(32, 10, activation='softmax')\n",
    "    \n",
    "    def construct(self, input_x):\n",
    "        output = self.flatten(input_x)\n",
    "        output = self.fc1(output)\n",
    "        output = self.fc2(output)\n",
    "        output = self.fc3(output)   \n",
    "        output = self.fc4(output)\n",
    "        output = self.fc5(output)\n",
    "        output = self.fc6(output)\n",
    "        return output "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#创建网络，损失函数，评估指标  优化器\n",
    "lr = 0.001\n",
    "num_epoch = 10\n",
    "momentum = 0.9\n",
    "\n",
    "net = ForwardNN()\n",
    "loss = nn.loss.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')\n",
    "metrics={\"Accuracy\": Accuracy()}\n",
    "opt = nn.Adam(net.trainable_params(), lr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============== Starting Training ==============\n",
      "epoch: 1 step: 1875, loss is 1.5270265\n",
      "epoch: 2 step: 1875, loss is 1.5560682\n",
      "epoch: 3 step: 1875, loss is 1.4910114\n",
      "epoch: 4 step: 1875, loss is 1.4923987\n",
      "epoch: 5 step: 1875, loss is 1.5842149\n",
      "epoch: 6 step: 1875, loss is 1.6798363\n",
      "epoch: 7 step: 1875, loss is 1.6174003\n",
      "epoch: 8 step: 1875, loss is 1.6174002\n",
      "epoch: 9 step: 1875, loss is 1.4924002\n",
      "epoch: 10 step: 1875, loss is 1.7736466\n"
     ]
    }
   ],
   "source": [
    "ds_eval = create_dataset(False, batch_size=32)\n",
    "model = Model(net, loss, opt, metrics)\n",
    "config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10)\n",
    "ckpoint_cb = ModelCheckpoint(prefix=\"checkpoint_lenet\", config=config_ck)\n",
    "\n",
    "#训练模型\n",
    "ds_train = create_dataset(batch_size=32)\n",
    "print(\"============== Starting Training ==============\")\n",
    "model.train(num_epoch, ds_train,callbacks=[LossMonitor()],dataset_sink_mode=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Accuracy': 0.8278245192307693}\n"
     ]
    }
   ],
   "source": [
    "#使用测试集评估模型，打印总体准确率\n",
    "metrics=model.eval(ds_eval)\n",
    "loss_cb = LossMonitor(per_print_times=1)\n",
    "time_cb = TimeMonitor(data_size=ds_train.get_dataset_size())\n",
    "print(metrics)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
